﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using Mono.Cecil;
using Mono.Cecil.Cil;
using Terraria.ModLoader.IO;
using Terraria.ModLoader.UI;

namespace Terraria.ModLoader
{
	// Token: 0x02000166 RID: 358
	internal class AssemblyManager
	{
		// Token: 0x06000EE3 RID: 3811 RVA: 0x0000E2D2 File Offset: 0x0000C4D2
		static AssemblyManager()
		{
			AppDomain.CurrentDomain.AssemblyResolve += delegate(object sender, ResolveEventArgs args)
			{
				string name = new AssemblyName(args.Name).Name;
				if (name == "Terraria")
				{
					return Assembly.GetExecutingAssembly();
				}
				Assembly result;
				AssemblyManager.loadedAssemblies.TryGetValue(name, out result);
				return result;
			};
		}

		// Token: 0x06000EE4 RID: 3812 RVA: 0x00400834 File Offset: 0x003FEA34
		private static void RecalculateReferences()
		{
			foreach (AssemblyManager.LoadedMod loadedMod in AssemblyManager.loadedMods.Values)
			{
				loadedMod.dependencies.Clear();
				loadedMod.dependents.Clear();
			}
			foreach (AssemblyManager.LoadedMod loadedMod2 in AssemblyManager.loadedMods.Values)
			{
				foreach (string text in loadedMod2.properties.RefNames(true))
				{
					if (AssemblyManager.loadedMods.ContainsKey(text))
					{
						loadedMod2.AddDependency(AssemblyManager.loadedMods[text]);
					}
					else
					{
						loadedMod2.weakDependencies.Add(text);
					}
				}
			}
			foreach (AssemblyManager.LoadedMod loadedMod3 in AssemblyManager.loadedMods.Values)
			{
				loadedMod3.UpdateWeakRefs();
			}
		}

		// Token: 0x06000EE5 RID: 3813 RVA: 0x00400984 File Offset: 0x003FEB84
		private static Assembly LoadAssembly(byte[] code, byte[] pdb = null)
		{
			Assembly assembly = Assembly.Load(code, pdb);
			AssemblyManager.loadedAssemblies[assembly.GetName().Name] = assembly;
			return assembly;
		}

		// Token: 0x06000EE6 RID: 3814 RVA: 0x004009B0 File Offset: 0x003FEBB0
		private static Mod Instantiate(AssemblyManager.LoadedMod mod)
		{
			Mod result;
			try
			{
				Type type = mod.assembly.GetTypes().SingleOrDefault((Type t) => t.IsSubclassOf(typeof(Mod)));
				if (type == null)
				{
					throw new Exception("It looks like this mod doesn't have a class extending Mod. Mods need a Mod class to function.")
					{
						HelpLink = "https://github.com/blushiemagic/tModLoader/wiki/Basic-tModLoader-Modding-FAQ#sequence-contains-no-matching-element-error"
					};
				}
				Mod mod2 = (Mod)Activator.CreateInstance(type);
				mod2.File = mod.modFile;
				mod2.Code = mod.assembly;
				mod2.Side = mod.properties.side;
				mod2.DisplayName = mod.properties.displayName;
				result = mod2;
			}
			catch (Exception ex)
			{
				ex.Data["mod"] = mod.Name;
				throw;
			}
			return result;
		}

		// Token: 0x06000EE7 RID: 3815 RVA: 0x00400A80 File Offset: 0x003FEC80
		internal static List<Mod> InstantiateMods(List<LocalMod> modsToLoad)
		{
			List<AssemblyManager.LoadedMod> modList = new List<AssemblyManager.LoadedMod>();
			foreach (LocalMod localMod in modsToLoad)
			{
				AssemblyManager.LoadedMod loadedMod;
				if (!AssemblyManager.loadedMods.TryGetValue(localMod.Name, out loadedMod))
				{
					loadedMod = (AssemblyManager.loadedMods[localMod.Name] = new AssemblyManager.LoadedMod());
				}
				loadedMod.SetMod(localMod);
				modList.Add(loadedMod);
			}
			AssemblyManager.RecalculateReferences();
			if (Debugger.IsAttached)
			{
				ModLoader.isModder = true;
				foreach (AssemblyManager.LoadedMod loadedMod2 in from mod in modList
				where mod.properties.editAndContinue && mod.CanEaC
				select mod)
				{
					loadedMod2.EnableEaC();
				}
			}
			if (ModLoader.alwaysLogExceptions)
			{
				ModCompile.ActivateExceptionReporting();
			}
			List<Mod> result;
			try
			{
				int i = 0;
				Parallel.ForEach<AssemblyManager.LoadedMod>(modList, delegate(AssemblyManager.LoadedMod mod)
				{
					UILoadMods loadMods = Interface.loadMods;
					string name = mod.Name;
					int i = i;
					i++;
					loadMods.SetProgressCompatibility(name, i, modsToLoad.Count);
					mod.LoadAssemblies();
				});
				result = modList.Select(new Func<AssemblyManager.LoadedMod, Mod>(AssemblyManager.Instantiate)).ToList<Mod>();
			}
			catch (AggregateException ex)
			{
				ErrorLogger.LogMulti(ex.InnerExceptions.Select(delegate(Exception e)
				{
					Func<AssemblyManager.LoadedMod, bool> <>9__4;
					return delegate
					{
						IEnumerable<AssemblyManager.LoadedMod> modList = modList;
						Func<AssemblyManager.LoadedMod, bool> predicate;
						if ((predicate = <>9__4) == null)
						{
							predicate = (<>9__4 = ((AssemblyManager.LoadedMod m) => m.Name == (string)e.Data["mod"]));
						}
						AssemblyManager.LoadedMod loadedMod4 = modList.Single(predicate);
						ModLoader.DisableMod(loadedMod4.Name);
						ErrorLogger.LogLoadingError(loadedMod4.Name, loadedMod4.modFile.tModLoaderVersion, e, false);
					};
				}));
				result = null;
			}
			catch (Exception ex2)
			{
				Exception e2 = ex2;
				Exception e = e2;
				AssemblyManager.LoadedMod loadedMod3 = modList.Single((AssemblyManager.LoadedMod m) => m.Name == (string)e.Data["mod"]);
				ModLoader.DisableMod(loadedMod3.Name);
				ErrorLogger.LogLoadingError(loadedMod3.Name, loadedMod3.modFile.tModLoaderVersion, e, false);
				result = null;
			}
			return result;
		}

		// Token: 0x04001289 RID: 4745
		private static readonly IDictionary<string, AssemblyManager.LoadedMod> loadedMods = new Dictionary<string, AssemblyManager.LoadedMod>();

		// Token: 0x0400128A RID: 4746
		private static readonly IDictionary<string, Assembly> loadedAssemblies = new Dictionary<string, Assembly>();

		// Token: 0x02000167 RID: 359
		private class LoadedMod
		{
			// Token: 0x1700011A RID: 282
			// (get) Token: 0x06000EE9 RID: 3817 RVA: 0x0000E302 File Offset: 0x0000C502
			public string Name
			{
				get
				{
					return this.modFile.name;
				}
			}

			// Token: 0x1700011B RID: 283
			// (get) Token: 0x06000EEA RID: 3818 RVA: 0x0000E30F File Offset: 0x0000C50F
			// (set) Token: 0x06000EEB RID: 3819 RVA: 0x0000E317 File Offset: 0x0000C517
			private bool NeedsReload
			{
				get
				{
					return this._needsReload;
				}
				set
				{
					if (value && !this._needsReload)
					{
						this.loadIndex++;
					}
					this._needsReload = value;
				}
			}

			// Token: 0x1700011C RID: 284
			// (get) Token: 0x06000EEC RID: 3820 RVA: 0x0000E339 File Offset: 0x0000C539
			private string AssemblyName
			{
				get
				{
					if (!this.eacEnabled)
					{
						return this.Name + "_" + this.loadIndex;
					}
					return this.Name;
				}
			}

			// Token: 0x06000EED RID: 3821 RVA: 0x00400CA8 File Offset: 0x003FEEA8
			private string DllName(string dll)
			{
				if (!this.eacEnabled)
				{
					return string.Concat(new object[]
					{
						this.Name,
						"_",
						dll,
						"_",
						this.loadIndex
					});
				}
				return dll;
			}

			// Token: 0x06000EEE RID: 3822 RVA: 0x0000E365 File Offset: 0x0000C565
			private string WeakDepName(string depName)
			{
				if (!this.eacEnabled)
				{
					return depName + "_0";
				}
				return depName;
			}

			// Token: 0x06000EEF RID: 3823 RVA: 0x00400CF8 File Offset: 0x003FEEF8
			public void SetMod(LocalMod mod)
			{
				if (this.modFile == null || this.modFile.version != mod.modFile.version || !this.modFile.hash.SequenceEqual(mod.modFile.hash))
				{
					this.SetNeedsReload();
				}
				this.modFile = mod.modFile;
				this.properties = mod.properties;
			}

			// Token: 0x06000EF0 RID: 3824 RVA: 0x00400D68 File Offset: 0x003FEF68
			private void SetNeedsReload()
			{
				this.NeedsReload = true;
				this.eacEnabled = false;
				foreach (AssemblyManager.LoadedMod loadedMod in this.dependents)
				{
					loadedMod.SetNeedsReload();
				}
			}

			// Token: 0x06000EF1 RID: 3825 RVA: 0x0000E37C File Offset: 0x0000C57C
			public void AddDependency(AssemblyManager.LoadedMod dep)
			{
				this.dependencies.Add(dep);
				dep.dependents.Add(this);
			}

			// Token: 0x1700011D RID: 285
			// (get) Token: 0x06000EF2 RID: 3826 RVA: 0x00400DC8 File Offset: 0x003FEFC8
			public bool CanEaC
			{
				get
				{
					if (this.eacEnabled)
					{
						return true;
					}
					if (!AssemblyManager.loadedAssemblies.ContainsKey(this.modFile.name))
					{
						return this.dependencies.All((AssemblyManager.LoadedMod dep) => dep.CanEaC);
					}
					return false;
				}
			}

			// Token: 0x06000EF3 RID: 3827 RVA: 0x00400E24 File Offset: 0x003FF024
			public void EnableEaC()
			{
				if (this.eacEnabled)
				{
					return;
				}
				this.SetNeedsReloadUnlessEaC();
				this.eacEnabled = true;
				foreach (AssemblyManager.LoadedMod loadedMod in this.dependencies)
				{
					loadedMod.EnableEaC();
				}
			}

			// Token: 0x06000EF4 RID: 3828 RVA: 0x00400E8C File Offset: 0x003FF08C
			private void SetNeedsReloadUnlessEaC()
			{
				if (!this.eacEnabled)
				{
					this.NeedsReload = true;
				}
				foreach (AssemblyManager.LoadedMod loadedMod in this.dependents)
				{
					loadedMod.SetNeedsReloadUnlessEaC();
				}
			}

			// Token: 0x06000EF5 RID: 3829 RVA: 0x00400EF0 File Offset: 0x003FF0F0
			public void UpdateWeakRefs()
			{
				foreach (AssemblyManager.LoadedMod loadedMod in from dep in this.dependencies
				where this.weakDependencies.Remove(dep.Name)
				select dep)
				{
					if (this.eacEnabled && !loadedMod.eacEnabled)
					{
						loadedMod.EnableEaC();
					}
					else if (loadedMod.AssemblyName != this.WeakDepName(loadedMod.Name))
					{
						this.SetNeedsReload();
					}
				}
			}

			// Token: 0x06000EF6 RID: 3830 RVA: 0x00400F80 File Offset: 0x003FF180
			public void LoadAssemblies()
			{
				if (!this.NeedsReload)
				{
					return;
				}
				try
				{
					this.modFile.Read(TmodFile.LoadedState.Code, null);
					foreach (string str in this.properties.dllReferences)
					{
						AssemblyManager.LoadAssembly(this.EncapsulateReferences(this.modFile.GetFile("lib/" + str + ".dll")), null);
					}
					this.assembly = AssemblyManager.LoadAssembly(this.EncapsulateReferences(this.modFile.GetMainAssembly(null)), this.modFile.GetMainPDB(null));
					this.NeedsReload = false;
				}
				catch (Exception ex)
				{
					ex.Data["mod"] = this.Name;
					throw;
				}
			}

			// Token: 0x06000EF7 RID: 3831 RVA: 0x00401058 File Offset: 0x003FF258
			private byte[] EncapsulateReferences(byte[] code)
			{
				if (this.eacEnabled)
				{
					return code;
				}
				AssemblyDefinition assemblyDefinition = AssemblyDefinition.ReadAssembly(new MemoryStream(code), new ReaderParameters
				{
					AssemblyResolver = AssemblyManager.TerrariaCecilAssemblyResolver.instance
				});
				assemblyDefinition.Name.Name = this.EncapsulateName(assemblyDefinition.Name.Name);
				assemblyDefinition.MainModule.Mvid = Guid.NewGuid();
				foreach (ModuleDefinition moduleDefinition in assemblyDefinition.Modules)
				{
					foreach (AssemblyNameReference assemblyNameReference in moduleDefinition.AssemblyReferences)
					{
						assemblyNameReference.Name = this.EncapsulateName(assemblyNameReference.Name);
					}
				}
				MemoryStream memoryStream = new MemoryStream();
				assemblyDefinition.Write(memoryStream, new WriterParameters
				{
					SymbolWriterProvider = AssemblyManager.SymbolWriterProvider.instance
				});
				return memoryStream.ToArray();
			}

			// Token: 0x06000EF8 RID: 3832 RVA: 0x0040116C File Offset: 0x003FF36C
			private string EncapsulateName(string name)
			{
				if (name == this.Name)
				{
					return this.AssemblyName;
				}
				if (this.properties.dllReferences.Contains(name))
				{
					return this.DllName(name);
				}
				if (this.weakDependencies.Contains(name))
				{
					return this.WeakDepName(name);
				}
				foreach (AssemblyManager.LoadedMod loadedMod in this.dependencies)
				{
					string text = loadedMod.EncapsulateName(name);
					if (text != name)
					{
						return text;
					}
				}
				return name;
			}

			// Token: 0x0400128B RID: 4747
			public TmodFile modFile;

			// Token: 0x0400128C RID: 4748
			public BuildProperties properties;

			// Token: 0x0400128D RID: 4749
			public readonly List<AssemblyManager.LoadedMod> dependencies = new List<AssemblyManager.LoadedMod>();

			// Token: 0x0400128E RID: 4750
			public readonly List<AssemblyManager.LoadedMod> dependents = new List<AssemblyManager.LoadedMod>();

			// Token: 0x0400128F RID: 4751
			public readonly ISet<string> weakDependencies = new HashSet<string>();

			// Token: 0x04001290 RID: 4752
			public Assembly assembly;

			// Token: 0x04001291 RID: 4753
			private int loadIndex;

			// Token: 0x04001292 RID: 4754
			private bool eacEnabled;

			// Token: 0x04001293 RID: 4755
			private bool _needsReload = true;
		}

		// Token: 0x02000169 RID: 361
		internal class TerrariaCecilAssemblyResolver : DefaultAssemblyResolver
		{
			// Token: 0x06000EFE RID: 3838 RVA: 0x0000E3ED File Offset: 0x0000C5ED
			private TerrariaCecilAssemblyResolver()
			{
				base.RegisterAssembly(ModuleDefinition.ReadModule(Assembly.GetExecutingAssembly().Location).Assembly);
			}

			// Token: 0x04001296 RID: 4758
			public static readonly AssemblyManager.TerrariaCecilAssemblyResolver instance = new AssemblyManager.TerrariaCecilAssemblyResolver();
		}

		// Token: 0x0200016A RID: 362
		internal class SymbolWriterProvider : ISymbolWriterProvider
		{
			// Token: 0x06000F00 RID: 3840 RVA: 0x0000E41B File Offset: 0x0000C61B
			public ISymbolWriter GetSymbolWriter(ModuleDefinition module, string fileName)
			{
				return new AssemblyManager.SymbolWriterProvider.HeaderCopyWriter(module);
			}

			// Token: 0x06000F01 RID: 3841 RVA: 0x0000E41B File Offset: 0x0000C61B
			public ISymbolWriter GetSymbolWriter(ModuleDefinition module, Stream symbolStream)
			{
				return new AssemblyManager.SymbolWriterProvider.HeaderCopyWriter(module);
			}

			// Token: 0x04001297 RID: 4759
			public static readonly AssemblyManager.SymbolWriterProvider instance = new AssemblyManager.SymbolWriterProvider();

			// Token: 0x0200016B RID: 363
			private class HeaderCopyWriter : ISymbolWriter, IDisposable
			{
				// Token: 0x06000F04 RID: 3844 RVA: 0x0000E42F File Offset: 0x0000C62F
				public HeaderCopyWriter(ModuleDefinition module)
				{
					this.module = module;
				}

				// Token: 0x06000F05 RID: 3845 RVA: 0x0000E43E File Offset: 0x0000C63E
				public bool GetDebugHeader(out ImageDebugDirectory directory, out byte[] header)
				{
					if (!this.module.HasDebugHeader)
					{
						directory = default(ImageDebugDirectory);
						header = null;
						return false;
					}
					directory = this.module.GetDebugHeader(ref header);
					return true;
				}

				// Token: 0x06000F06 RID: 3846 RVA: 0x00006B58 File Offset: 0x00004D58
				public void Write(MethodBody body)
				{
				}

				// Token: 0x06000F07 RID: 3847 RVA: 0x00006B58 File Offset: 0x00004D58
				public void Write(MethodSymbols symbols)
				{
				}

				// Token: 0x06000F08 RID: 3848 RVA: 0x00006B58 File Offset: 0x00004D58
				public void Dispose()
				{
				}

				// Token: 0x04001298 RID: 4760
				private ModuleDefinition module;
			}
		}
	}
}
